from django.http import HttpResponse
from django.shortcuts import get_object_or_404
from django.utils.encoding import is_protected_type, smart_unicode

from django.conf.urls.defaults import url, patterns, include

from assets.util import HttpResponseSerialized



def Resource(model, pk_name, exclude_read=[], serializer=lambda x: x, decorator=lambda x: x):
  class Resource():
    def __init__(self, field):
      self.field = field
      self.selected_fields = None
    
    def all(self, request):
      data = []
      for rule in model.objects.all():
        data.append(serializer(rule))
      return HttpResponseSerialized(request, data)
    all.__doc__ = 'Get all %s instances.'%(model.__name__)
    
       
    def get(self, request, pk):
      object = get_object_or_404(model, **{pk_name:pk}) 
      object = serializer(object)
      return HttpResponseSerialized(request, object)
    get.__doc__ = 'Get a %s instance by "%s".'%(model.__name__, pk_name)
    
    
    def create(self, request, pk):
      object = model()
      setattr(object, pk_name, pk)
      object.save()
      return HttpResponse('success')
    create.__doc__ = 'Create a new %s instance by specifying "%s".'%(model.__name__, pk_name)
      
    def delete(self, request, pk):
      object = get_object_or_404(model, **{pk_name:pk}) 
      object.delete()
      return HttpResponse('success')
    delete.__doc__ = 'Delete a %s instance by "%s".'%(model.__name__, pk_name)
        
    def get_field(self, request, pk):
      object = get_object_or_404(model, **{pk_name:pk}) 
      #for field in object._meta.local_fields:
      field,a,b,c = object._meta.get_field_by_name(self.field)
      value = None
      if field:
        if field.rel is None:
          value = handle_field(object, field)
        else:
          value = handle_fk_field(object, field)    
      return HttpResponseSerialized(request, value)
    get_field.__doc__ = 'Get the value of the specified field from a %s instance specified by "%s"'%(model.__name__, pk_name)
    
    def update_field(self, request, pk, value):
      object = get_object_or_404(model, **{pk_name:pk}) 
      setattr(object, self.field, value)
      object.save()
      return HttpResponse('success '+self.field)
    update_field.__doc__ = 'Set the value of the specified field from a %s instance specified by "%s"'%(model.__name__, pk_name)
    
    def get_m2m_field(self, request, pk):
      object = get_object_or_404(model, **{pk_name:pk})                          
      field,a,b,c = object._meta.get_field_by_name(self.field)
      values = handle_m2m_field(object, field)
      return HttpResponseSerialized(request, values)
    get_m2m_field.__doc__ = 'Get the values of the specified m2m field from a %s instance specified by "%s"'%(model.__name__, pk_name)
    
    def m2m_add(self, request, pk, pk_value):
      object = get_object_or_404(model, **{pk_name:pk})
      field,a,b,c = object._meta.get_field_by_name(self.field)
      other = get_object_or_404(field.rel.to, **{field.rel.to._meta.pk.name:pk_value})
      getattr(object, self.field).add(other)
      return HttpResponse('success '+self.field+' '+pk_value )
    m2m_add.__doc__ = 'Add to the values of the specified m2m field from a %s instance specified by "%s"'%(model.__name__, pk_name)
    
    def m2m_remove(self, request, pk, pk_value):
      object = get_object_or_404(model, **{pk_name:pk})
      field,a,b,c = object._meta.get_field_by_name(self.field)
      other = get_object_or_404(field.rel.to, **{field.rel.to._meta.pk.name:pk_value})
      getattr(object, self.field).remove(other)
      return HttpResponse('success '+self.field+' '+pk_value )
    m2m_remove.__doc__ = 'Remove from the values of the specified m2m field from a %s instance specified by "%s"'%(model.__name__, pk_name)

    
  urls = []  
  urls.append(url(r'^$', decorator(Resource(None).all)))
  urls.append(url(r'^(?P<pk>[^/]+?)/?$', decorator(Resource(None).get)))
  urls.append(url(r'^create/(?P<pk>[^/]+?)/?$', decorator(Resource(None).create)))
  urls.append(url(r'^(?P<pk>[^/]+?)/delete/?$', decorator(Resource(None).delete)))
  for field in [field.name for field in model._meta.local_fields]:
    if field != model._meta.pk.name:
      if field not in exclude_read:
        urls.append(url(r'^(?P<pk>[^/]+?)/'+field+'/?$', decorator(Resource(field).get_field)))
      urls.append(url(r'^(?P<pk>[^/]+?)/'+field+'/(?P<value>[^/]+?)/?$', decorator(Resource(field).update_field)))
  for field in [field.name for field in model._meta.many_to_many]:
    if field not in exclude_read:
      urls.append(url(r'^(?P<pk>[^/]+?)/'+field+'/?$', decorator(Resource(field).get_m2m_field)))
    urls.append(url(r'^(?P<pk>[^/]+?)/'+field+'/add/(?P<pk_value>.+)/?$', decorator(Resource(field).m2m_add)))
    urls.append(url(r'^(?P<pk>[^/]+?)/'+field+'/remove/(?P<pk_value>[^/]+?)/?$', decorator(Resource(field).m2m_remove)))
  return include(patterns('', *urls))
  
  
  
  
def handle_field(obj, field):
  value = field._get_val_from_obj(obj)
  if is_protected_type(value):
    return value
  else:
    return field.value_to_string(obj)
      
def handle_fk_field(obj, field):
  related = getattr(obj, field.name)
  if related is not None:
    if hasattr(related, 'natural_key'):
      related = related.natural_key()
    else:
      if field.rel.field_name == related._meta.pk.name:
        related = related._get_pk_val()
      else:
        related = smart_unicode(getattr(related, field.rel.field_name), strings_only=True)
  return related
  
def handle_m2m_field(obj, field):
  if field.rel.through._meta.auto_created:
    if hasattr(field.rel.to, 'natural_key'):
      m2m_value = lambda value: value#.natural_key()
    else:
      m2m_value = lambda value: smart_unicode(value._get_pk_val(), strings_only=True)
    return [m2m_value(related) for related in getattr(obj, field.name).iterator()]
